package com.heima.media.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.aliyun.GreenImageScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.dto.ResponseResult;
import com.heima.common.minio.MinIOService;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.media.dto.ArticleDto;
import com.heima.media.dto.ContentDto;
import com.heima.media.dto.ImageDto;
import com.heima.media.entity.WmChannel;
import com.heima.media.entity.WmNews;
import com.heima.media.entity.WmSensitive;
import com.heima.media.entity.WmUser;
import com.heima.media.feign.ArticleFeign;
import com.heima.media.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class AuditServiceImpl implements IAuditService {
    @Lazy // 懒加载，解决循环引用问题 错误原因关键字：circular reference循环引用
    @Autowired
    private IWmNewsService wmNewsService;
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private ArticleFeign articleFeign;
    @Autowired
    private MinIOService minIOService;
    @Autowired
    private IWmUserService wmUserService;
    @Autowired
    private IWmChannelService channelService;
    @Autowired
    private IWmSensitiveService wmSensitiveService;

    // 延迟任务
    @Autowired
    private RedissonClient redissonClient;


    /**
     * 自媒体文章阿里云审核测试
     * @param id
     */
    @Override
    public void audit(Integer id) {
        audit(wmNewsService.getById(id));
    }

    @Async // 多线程异步方案，注解@Async+启动类开启@EnableAsync
    @Override
    public void audit(WmNews wmNews) {
        boolean isPass = true;
        // 新增：敏感词审核
        isPass = checkSensitive(wmNews);
        log.info("[文章敏感词审核======>审核结果：{}]",isPass);
        if (!isPass) return;
        // 阿里云文本审核
        isPass = checkText(wmNews);
        if (!isPass) return;
        // 阿里云的图片审核
        isPass = checkImage(wmNews);
        if (!isPass) return;
        // 文本和图片都审核通过
        // 保存app的文章
        // 判断现在的时间是不是已经到达了发布时间
        if (System.currentTimeMillis() >= wmNews.getPublishTime().getTime()) {
            // 审核通过并发布
            // 远程调用，保存app已发布的文章
            Long articleId = saveArticle(wmNews);
            wmNews.setArticleId(articleId);
            wmNews.setStatus(9);
        } else {
            // 审核通过待发布
            wmNews.setStatus(8);
            // 添加延迟任务
            RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque("article-bqueue");
            RDelayedQueue<Object> rDelayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            // 时间差=预期发布时间-当前时间
            Long time = wmNews.getPublishTime().getTime()-System.currentTimeMillis();
            rDelayedQueue.offer(wmNews.getId(),time, TimeUnit.MILLISECONDS);
            log.info("[延迟任务待发布======>延迟时间：{}，预计发布时间：{}]",time,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(wmNews.getPublishTime().getTime()));
        }
        wmNewsService.updateById(wmNews);
    }

    /**
     * 远程调用保存已发布的文章
     * @param wmNews
     * @return
     */
    public Long saveArticle(WmNews wmNews) {
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, articleDto);
        articleDto.setId(null); // 保险起见
        // app用户可以申请成自媒体用户,自媒体用户自动就是文章作者
        // 获取自媒体用户，自媒体用户中有AuthorId
        Integer userId = wmNews.getUserId();
        WmUser wmUser = wmUserService.getById(userId);
        articleDto.setAuthorId(wmUser.getApAuthorId());
        articleDto.setAuthorName(wmUser.getName());
        articleDto.setChannelId(wmNews.getChannelId());
        WmChannel wmChannel = channelService.getById(wmNews.getChannelId());
        articleDto.setChannelName(wmChannel.getName());
        articleDto.setLayout(wmNews.getType());
        articleDto.setFlag(0); //平台文章
        List<ImageDto> imageDtoList = JSON.parseArray(wmNews.getImages(), ImageDto.class);
        List<String> urlList = new ArrayList<>();
        for (ImageDto imageDto : imageDtoList) {
            urlList.add(imageDto.getUrl());
        }
        // articleDto.setImages(JSON.toJSONString(urlList)); // 格式不同
        articleDto.setImages(String.join(",",urlList));
        log.info("[图片格式检测======>自媒体文章图片格式转换，文章内容：{}]",articleDto.toString());
        ResponseResult<Long> longResponseResult = articleFeign.saveArticle(articleDto);
        Long articleId = longResponseResult.getData();
        return articleId;
    }

    /**
     * 敏感词审核
     * @param wmNews
     * @return
     */
    private boolean checkSensitive(WmNews wmNews) {
        // 获取文章文本
        String text = getText(wmNews);
        // 敏感词汇总
        List<WmSensitive> sensitiveList = wmSensitiveService.list();
        List<String> sensitiveWords = new ArrayList<>();
        for (WmSensitive wmSensitive : sensitiveList) {
            sensitiveWords.add(wmSensitive.getSensitives());
        }
        // DFA，初始化敏感词
        SensitiveWordUtil.initMap(sensitiveWords);
        // 对比，获取匹配的关键词和命中次数
        Map<String, Integer> stringIntegerMap = SensitiveWordUtil.matchWords(text);
        if (stringIntegerMap.size()>0) {
            Set<String> strings = stringIntegerMap.keySet();
            String reason = String.join(",", strings);
            wmNews.setStatus(2);
            wmNews.setReason("敏感词审核未通过，原因是包含以下敏感词：" +reason); //审核不通过或需要人工审核的原因
            wmNewsService.updateById(wmNews);
            return false;
        }else {
            return true;
        }
    }

    /**
     * aliyun自动审核文本
     *
     * @param wmNews
     * @return
     */
    private boolean checkText(WmNews wmNews) {
        // 获取文章文本
        String text = getText(wmNews);

        boolean isPass = false;
        try {
            Map map = greenTextScan.greenTextScan(text);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                // suggestion
                // pass：文本正常，可以直接放行。
                // review：文本需要进一步人工审核。             label=porn
                // block：文本违规，可以直接删除或者限制公开。  label=porn
                case "pass":
                    isPass = true;
                    break;
                case "review":
                    wmNews.setStatus(3);
                    wmNews.setReason("阿里云文本审核未通过，需要人工审核，原因是：" + map.get("label")); //审核不通过或需要人工审核的原因
                    wmNewsService.updateById(wmNews);
                    isPass = false;
                    break;
                case "block":
                    wmNews.setStatus(2);
                    wmNews.setReason("阿里云文本审核未通过，原因是：" + map.get("label")); //审核不通过或需要人工审核的原因
                    wmNewsService.updateById(wmNews);
                    isPass = false;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isPass;
    }

    /**
     * aliyun自动审核图片
     *
     * @param wmNews
     * @return
     */
    private boolean checkImage(WmNews wmNews) {
        // 注意图片审核包括两部分：文章内容图片+文章的封面图片
        // 封面图片
        String coverImages = wmNews.getImages(); //获取封面图片
        List<ImageDto> imageDtoList = JSON.parseArray(coverImages, ImageDto.class);
        // 文章内容
        String content = wmNews.getContent();
        List<ContentDto> contentDtos = JSON.parseArray(content, ContentDto.class);
        for (ContentDto contentDto : contentDtos) {
            if (contentDto.getType().equals("image")) { //获取文章内容图片
                ImageDto imageDto = new ImageDto();
                imageDto.setId(contentDto.getId());
                imageDto.setUrl(contentDto.getValue());
                if (!imageDtoList.contains(imageDto)) { //判断是否有重复，如果有就不再放入了
                    imageDtoList.add(imageDto);
                }
            }
        }

        boolean isPass = false;
        try {
            // 将图片转成字节数组
            List<byte[]> imageList = new ArrayList<>();
            for (ImageDto imageDto : imageDtoList) {
                InputStream inputStream = minIOService.download(imageDto.getUrl());
                byte[] bytes = IOUtils.toByteArray(inputStream);
                imageList.add(bytes);
            }
            Map map = greenImageScan.imageScan(imageList);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "pass":
                    isPass = true;
                    break;
                case "review":
                    wmNews.setStatus(3);
                    wmNews.setReason("阿里云图片审核未通过，需要人工审核，原因是：" + map.get("label")); //审核不通过或需要人工审核的原因
                    wmNewsService.updateById(wmNews);
                    isPass = false;
                    break;
                case "block":
                    wmNews.setStatus(2);
                    wmNews.setReason("阿里云图片审核未通过，原因是：" + map.get("label")); //审核不通过或需要人工审核的原因
                    wmNewsService.updateById(wmNews);
                    isPass = false;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isPass;
    }

    /**
     * 获取文章文本内容
     * @param wmNews
     * @return
     */
    private String getText(WmNews wmNews) {
        // 注意文本包括：文章标题+文章的内容
        String text = wmNews.getTitle();
        String content = wmNews.getContent();
        List<ContentDto> contentDtos = JSON.parseArray(content, ContentDto.class);
        for (ContentDto contentDto : contentDtos) {
            if (contentDto.getType().equals("text")) {
                text += contentDto.getValue();
            }
        }
        return text;
    }
}
